
"""
参考:
1. llvm command python函数定义 :
    https://lldb.llvm.org/use/python-reference.html#create-a-new-lldb-command-using-a-python-function
2. 一个估计是旧版本lldb command python例子. 本程序最初从这复制的
    https://cloud.tencent.com/developer/ask/sof/108628121

usage:
lldb -- `which ls` /
    command script import /pubx/analyze_code/lldb_demo/PrintMemoryRegions.py
    command script add -f PrintMemoryRegions.llvmCmdPrintMemoryRegions printMemoryRegions
    breakpoint set --name __GI___libc_malloc
    run
    #然后听在断点处, 输入以下命令:
    printMemoryRegions
"""
from typing import List

import lldb

class MemRegion:
    def __init__(self,beginAddr:int,endAddr:int,regionSize:int,IsReadable:bool,IsWritable:bool,IsExecutable:bool,IsMapped:bool):
        self.beginAddr:int=beginAddr
        self.endAddr:int=endAddr
        self.regionSize:int=regionSize
        self.IsReadable: bool = IsReadable
        self.IsWritable: bool = IsWritable
        self.IsExecutable: bool = IsExecutable
        self.IsMapped: bool = IsMapped
        self.memRegionBytes:bytes=None
        self.memRegionByteArray:bytearray=None

        self.readErrType:int=None
        self.readErr:int=None
        self.readErrMsg:str=None

    @staticmethod
    def title():
        return "beginAddr,endAddr,regionSize,_memoryDataLen,IsReadable,IsWritable,IsExecutable,IsMapped,_readErrType,_readErr,_readErrMsg"
    def __str__(self):
        _memoryDataLen:str='' if self.memRegionBytes is None else len(self.memRegionBytes)
        _readErrType:str='' if self.readErrType is None else self.readErrType
        _readErr:str='' if self.readErr is None else _int2HexStr(self.readErr)
        _readErrMsg:str='' if self.readErrMsg is None else f'"{self.readErrMsg}"'
        return f"""{_int2HexStr(self.beginAddr)},{_int2HexStr(self.endAddr)},{self.regionSize},{_memoryDataLen},{self.IsReadable},{self.IsWritable},{self.IsExecutable},{self.IsMapped},{_readErrType},{_readErr},{_readErrMsg}"""

def llvmCmdPrintMemoryRegions(debugger:lldb.SBDebugger, command:str, result:lldb.SBCommandReturnObject, internal_dict:dict):
    """
    llvm command python函数定义 参考: https://lldb.llvm.org/use/python-reference.html#create-a-new-lldb-command-using-a-python-function
    :param debugger:
    :param command:
    :param result:
    :param internal_dict:
    :return:
    """
    target : lldb.SBTarget  = debugger.GetSelectedTarget()
    process:lldb.SBProcess = target.GetProcess()


    memRegions:lldb.SBMemoryRegionInfoList = process.GetMemoryRegions()
    regionCnt:int = memRegions.GetSize()
    print(MemRegion.title())
    for k in range(regionCnt):
        # print("\n")
        regionKOut:lldb.SBMemoryRegionInfo = lldb.SBMemoryRegionInfo()
        success:bool = memRegions.GetMemoryRegionAtIndex(k, regionKOut)
        if not success:
            print(f"GetMemoryRegionAtIndex({k}):not success")
            continue

        # if success:
        regionEntityK: MemRegion=None;  idxFound: int=None
        regionEntityK,idxFound=_printMemoryRegionK(process, regionKOut)
        print(f"regionEntity{k}:",regionEntityK)
        if idxFound!=Search.IDX_NONE:
            addrFound:int=regionEntityK.beginAddr + idxFound
            print(f"regionEntity{k} found 目标4字节0XDFDFDFDF in index 十六进制:{_int2HexStr(idxFound)} 十进制:{idxFound}; 可以用lldb命令确认: memory read -s4 -fx -c1  {_int2HexStr(addrFound)} --force")
            #lldb 读内存地址0x7FFFF6EBC95E开始的4字节 展示为十六进制 命令:  memory read -s4 -fx -c1  0x7FFFF6EBC95E --force



def _printMemoryRegionK(process:lldb.SBProcess, regionK:lldb.SBMemoryRegionInfo)->MemRegion:
    idxFound: int=Search.IDX_NONE

    beginAddr:int = regionK.GetRegionBase()
    endAddr:int = regionK.GetRegionEnd()
    regionSize:int = endAddr-beginAddr

    IsReadable:bool=regionK.IsReadable()
    IsWritable:bool=regionK.IsWritable()
    IsExecutable:bool=regionK.IsExecutable()
    IsMapped:bool=regionK.IsMapped()

    memRegionK:MemRegion=MemRegion(beginAddr, endAddr, regionSize, IsReadable, IsWritable, IsExecutable, IsMapped)
    if not IsReadable:
        # print("memRegionK is not readable.")
        return (memRegionK,idxFound)

    # if IsReadable:
    readErr:lldb.SBError = lldb.SBError()
    memRegionBytes:bytes = process.ReadMemory(beginAddr, regionSize, readErr)

    """
    ReadMemory 调用例子参照:   
        https://lldb.llvm.org/python_reference/lldb.SBProcess-class.html#ReadMemory
            def ReadMemory(self, *args):
            '''
            # Read 4 bytes from address 'addr' and assume error.Success() is True. 
            content = process.ReadMemory(addr, 4, error) 
            new_bytes = bytearray(content) 
            '''
    """
    if readErr.Success():
        memRegionK.memRegionBytes:bytes=memRegionBytes
        # memRegionK.memRegionByteArray:bytearray=bytearray(memRegionBytes)
        idxFound:int=Search._searchMem(memRegionBytes)
    else:
        memRegionK.readErrType:int=readErr.GetType()
        memRegionK.readErr:int=readErr.GetError()
        memRegionK.readErrMsg:str=readErr.__str__()


    return (memRegionK,idxFound)

def _int2HexStr(memAddr:int):
    return '0x{:016x}'.format(memAddr)


class Search:
    IDX_NONE:int=-1
    @staticmethod
    def _searchMem(memRegionBytes:bytes )->int:
        _BYTE_CNT=4
        _BYTES=[0xDF,0XDF,0XDF,0XDF]
        for k in range(len(memRegionBytes)- (_BYTE_CNT-1) ):
            byte0=memRegionBytes[k]
            byte1=memRegionBytes[k+1]
            byte2=memRegionBytes[k+2]
            byte3=memRegionBytes[k+3]
            if byte0==_BYTES[0] and  byte1==_BYTES[1] and  byte2==_BYTES[2] and  byte3==_BYTES[3]:
                return k

        return Search.IDX_NONE